#include "NetworkPlayer.h"
#include "NetworkManager.h"
#include "Transformation.h"
#include "Animation.h"
#include "AudioSource.h"
#include "AudioControl.h"
#include "BoxCollider.h"
#include "ThirdPersonController.h"
#include "NetworkController.h"
#include "Logger.h"
#include "Helpers.hpp"

using namespace game_engine;
using namespace game_engine::library;
using namespace game_engine::components;
using namespace game_engine::utils;

using namespace RakNet;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

NetworkPlayer::NetworkPlayer(void)
    : GameObject()
{
}

NetworkPlayer::NetworkPlayer(Scene *scn, const string &n, const string &t, const DrawLevelType d, 
    const Point &pos, RakNet::RPC3 *rpcFromNetwork)
    : GameObject(scn, n, t, d)
{
    // Configure network properties
    NetworkManager *networkManager = NetworkManager::getInstance();
    SetNetworkIDManager(&(networkManager->getNetworkIDManager()));
    if (rpcFromNetwork != 0)
    {
        SetNetworkID(NETPLAYER_AVATAR_ID);
    }
    RPC3_REGISTER_FUNCTION(networkManager->getRpc3Inst(), &NetworkPlayer::setRemoteDirection);

    // Create its components
    Transformation *playerTransf = new Transformation(this, pos.getX(), pos.getY(), 
        0.0, 0.0, 1.0, 1.0);
    AudioSource *playerAudio = new AudioSource(this, "footsteps", EFFECT);
    playerAudio->setAudioFile("footsteps.ogg");
    playerAudio->setLoop(true);
    AudioControl *audioControl = new AudioControl(this);

    BoxCollider *playerCollider = new BoxCollider(this, Size(32.0f, 64.0f));

    Animation *playerAnim;
    if (rpcFromNetwork == 0)
    {
        playerAnim = new Animation(this, "animations.xml", "player.png");
        ThirdPersonController *playerController = new ThirdPersonController(this);
        playerController->setIdleAnimationId("idle");
        playerController->setWalkAnimationId(EAST, "walkright");
        playerController->setWalkAnimationId(NORTH, "walkup");
        playerController->setWalkAnimationId(WEST, "walkleft");
        playerController->setWalkAnimationId(SOUTH, "walkdown");
        playerController->setWalkSpeed(25.0f);

        addComponent(playerController);
    }
    else
    {
        playerAnim = new Animation(this, "animations.xml", "enemy.png");
        NetworkController *playerController = new NetworkController(this);
        playerController->setIdleAnimationId("idle");
        playerController->setWalkAnimationId(EAST, "walkright");
        playerController->setWalkAnimationId(NORTH, "walkup");
        playerController->setWalkAnimationId(WEST, "walkleft");
        playerController->setWalkAnimationId(SOUTH, "walkdown");
        playerController->setWalkSpeed(25.0f);

        addComponent(playerController);
    }

    // Add components to object
    addComponent(playerTransf);
    addComponent(playerCollider);
    addComponent(playerAnim);
    addComponent(playerAudio);
    addComponent(audioControl);
}

NetworkPlayer::~NetworkPlayer(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

//INT32 NetworkPlayer::directionUpdated(INT32 dir)
//{
//    setRemoteDirection(dir, 0);
//    return 0;
//}

void NetworkPlayer::setRemoteDirection(const Direction dir, RPC3 *rpcFromNetwork)
{
    if (rpcFromNetwork == 0)
    {
        NetworkManager *networkManager = NetworkManager::getInstance();
        (networkManager->getRpc3Inst())->CallCPP("&NetworkPlayer::setRemoteDirection", 
            NETPLAYER_AVATAR_ID, dir, rpcFromNetwork);
    }
    else
    {
        string info("setRemoteDirection called from ");
        info.append(rpcFromNetwork->GetLastSenderAddress().ToString());
		Logger::INFO(GAMEENGINE_LOG, info);
        Logger::INFO(GAMEENGINE_LOG, string("new direction : ") + to_string<INT32>(dir, dec));

        // Retrieve NetworkController component and update its direction
        NetworkController *controller = dynamic_cast<NetworkController*> (getComponent("NetworkController"));
        controller->setDirection(dir);
    }
}

// FIXME : esto es muy feo, pero el mecanismo signal-slot no me ha funcionado por algun motivo...
void NetworkPlayer::update(const FLOAT32 time)
{
    // Retrieve ThirdPersonController component
    if (hasComponent("ThirdPersonController"))
    {
        ThirdPersonController *controller = 
            dynamic_cast<ThirdPersonController*> (getComponent("ThirdPersonController"));

        // Check whether its direction has been modified
        if (controller->getDirectionUpdated())
        {
            Direction dir = controller->getPrevDirection();
            setRemoteDirection(dir, 0);
        }
    }

    // Call update method from parent class
    GameObject::update(time);
}
